#include "Graphics/OGLGraphics.hpp"
#include "Graphics/Matrix.hpp"

namespace Graph
{

OGLGraphics::OGLGraphics()
{
}

void OGLGraphics::drawLine(const Vertex &x1, const Vertex &x2)
{
    // � аспаковываем цвет
    Color color1 = fromIntColor(x1.color);
    Color color2 = fromIntColor(x2.color);

    glBegin(GL_LINES);

    glColor4b(color1.getR(), color1.getG(), color1.getB(), color1.getA());
    glVertex3f(x1.x, x1.y, x1.z);

    glColor4b(color2.getR(), color2.getG(), color2.getB(), color2.getA());
    glVertex3f(x2.x, x2.y, x2.z);

    glEnd();
}

void OGLGraphics::drawArrays(const VertexVector &vertices, const GLenum mode)
{
    // включить режим массива вершин и цветов
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    const uint8_t * byteDataPtr = (const uint8_t*)vertices.data();

    // 3х-мерные точки, каждая координата это 1 float
    glVertexPointer(3, GL_FLOAT, sizeof(Vertex), byteDataPtr);

    // 4 компоненты цвета, каждая компонента это 1 байт
    glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), byteDataPtr + 3 * sizeof(float));

    // рисуем мн-во примитивов
    glDrawArrays(mode, 0, vertices.size());

    // отключаем режимы
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
}

void OGLGraphics::drawPolyLine(const VertexVector &vertices)
{
    // кол-во точек
    uint32_t dim = vertices.size();
    if (dim > 1)
    {
        drawArrays(vertices, GL_LINE_STRIP);
    }
}

void OGLGraphics::drawTriangle(const Vertex &a, const Vertex &b, const Vertex &c)
{
    // � аспаковываем цвет
    Color color1 = fromIntColor(a.color);
    Color color2 = fromIntColor(b.color);
    Color color3 = fromIntColor(c.color);

    glBegin(GL_TRIANGLES);

    glColor4b(color1.getR(), color1.getG(), color1.getB(), color1.getA());
    glVertex3f(a.x, a.y, a.z);

    glColor4b(color2.getR(), color2.getG(), color2.getB(), color2.getA());
    glVertex3f(b.x, b.y, b.z);

    glColor4b(color3.getR(), color3.getG(), color3.getB(), color3.getA());
    glVertex3f(c.x, c.y, c.z);

    glEnd();
}

void OGLGraphics::drawTriangles(const VertexVector &vertices)
{
    // кол-во точек
    uint32_t dim = vertices.size();
    if (dim > 2)
    {
        drawArrays(vertices, GL_TRIANGLES);
    }
}

void OGLGraphics::drawTriangleStrip(const VertexVector &vertices)
{
    // кол-во точек
    uint32_t dim = vertices.size();
    if (dim > 2)
    {
        drawArrays(vertices, GL_TRIANGLE_STRIP);
    }
}

void OGLGraphics::drawTriangleFan(const VertexVector &vertices)
{
    // кол-во точек
    uint32_t dim = vertices.size();
    if (dim > 2)
    {
        drawArrays(vertices, GL_TRIANGLE_FAN);
    }
}

void OGLGraphics::clear()
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

void OGLGraphics::clearColor(const Color &color)
{
    //float r = backgroundColor.getR() / 255.0f;
    //float g = backgroundColor.getG() / 255.0f;
    //float b = backgroundColor.getB() / 255.0f;
    //float a = backgroundColor.getA() / 255.0f;
    glClearColor(color.getR(), color.getG(), color.getB(), color.getA());
}

void OGLGraphics::flush()
{
    glFlush();
    glFinish();
}

void OGLGraphics::setViewport(int32_t x, int32_t y, int32_t width, int32_t height)
{
    glViewport(x, y, width, height);
}

void OGLGraphics::scale(const Vertex &scaleVertex)
{
    glScalef(scaleVertex.x, scaleVertex.y, scaleVertex.z);
}

void OGLGraphics::move(const Vertex &moveVertex)
{
    glTranslatef(moveVertex.x, moveVertex.y, moveVertex.z);
}

void OGLGraphics::rotate(const Vertex &axis, const float angle)
{
    glRotatef(angle, axis.x, axis.y, axis.z);
}

void OGLGraphics::setLineWidth(const float lineWidth)
{
    glLineWidth(lineWidth);
}


float OGLGraphics::getLineWidth() const
{
    float width = 1.0f;
    glGetFloatv(GL_LINE_WIDTH, &width);
    return width;
}

Graphics::MatrixMode OGLGraphics::getMatrixMode() const
{
    GLint mode = -1;
    glGetIntegerv(GL_MATRIX_MODE, &mode);
    switch (mode)
    {
    case GL_MODELVIEW:
        return Graphics::MODELVIEW;

    case GL_PROJECTION:
        return Graphics::PROJECTION;

    case GL_TEXTURE:
        return Graphics::TEXTURE;

    case GL_COLOR:
        return Graphics::COLOR;

    default:
        return Graphics::UNDEFINED;
    }
}

void OGLGraphics::setMatrixMode(const MatrixMode matrixMode)
{
    switch (matrixMode)
    {
    case Graphics::MODELVIEW:
        glMatrixMode(GL_MODELVIEW);
        break;

    case Graphics::PROJECTION:
        glMatrixMode(GL_PROJECTION);
        break;

    case Graphics::TEXTURE:
        glMatrixMode(GL_TEXTURE);
        break;

    case Graphics::COLOR:
        glMatrixMode(GL_COLOR);
        break;
    }
}

void OGLGraphics::loadIdentity()
{
    glLoadIdentity();
}

void OGLGraphics::ortho(const float xLeft, const float xRight,
                        const float yBottom, const float yTop,
                        const float zNear, const float zFar)
{
    glOrtho(xLeft, xRight, yBottom, yTop, zNear, zFar);
}

OGLGraphics::~OGLGraphics()
{
}

}
